//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#pragma once

#include "Platforms/Base/IApplication.h"

#if defined( PLATFORM_ANDROID ) or defined( ANDROID_EMULATOR )

#include "Platforms/Android/ASensorManager.h"
#include "Platforms/Android/AHwCamera.h"
#include "Platforms/Android/AApplication.h"
#include "Platforms/Base/DeviceWrapper.h"
#include "Base/Utils/Time/TimeProfiler.h"
#include "Graphics/GLES/opengles.h"
#include "Platforms/Android/AWindowSurface.h"

#include <stl_ext/OS/EGL/ContextEGL.h>


namespace Engine
{
namespace Platforms
{

#	ifdef PLATFORM_ANDROID
		typedef _platform_::ContextEGL	ContextEGL;
#	endif

	class AndroidJavaInterface;



	//
	// Android Platform
	//

	class AndroidPlatform : public IParallelThread, public IPlatform
	{
	// types
	public:
		struct Extension
		{
			enum type
			{
				CAMERA = 1,
			};
		};


	private:
		SHARED_POINTER( AndroidPlatform );

		AUTO_POINTER( AndroidJavaInterface );

		typedef struct __surface		surface_t;
		
		typedef void (* MessageCallback_t) (const BaseObjectPtr &obj, int result);
		
		typedef map< uint, OnMessageResult_t >	msg_callbacks_t;

		typedef static_array< string, EDirectory::_COUNT >	directories_t;


	// variables
	protected:
		// ui thread //
		AndroidSensorManager		_sensorManager;
		AndroidJavaInterfacePtr		_javaInterface;
		AndroidHwCamera				_camera;

		EventSystem					_events;

		CurrentThread				_thread;
		SyncEvent					_onThreadStarted;
		ThreadMessageQueue			_messageQueue;

		uvec2						_windowSize;
		bool						_onExitLoop;		// while true process messages in destructor


		// render thread //
		Ptr< IApplication >			_application;

		int64_array_t				_vibroPattern;

		Thread						_renderThread;
		ContextEGL					_renderContext;
		AndroidWindowSurface		_surface;
		
		msg_callbacks_t				_msgCallbacks;
		uint						_msgID;

		directories_t				_directories;
		
		TimeProfiler<uint64>		_timer;

		bool						_quitRenderThread;
		bool						_glesInitialized;

		EDevice::type				_requiredDeviceType;


	private:
		static AndroidPlatform *	_instance;


	// methods
	public:
		AndroidPlatform ();
		~AndroidPlatform ();

		void SendEvent (const SysEvent &ev);
		void SendEventInRenderThread (const SysEvent &ev);

		void PushToRenderThread (const ParallelOp &op);
		RVALUE_REFERENCE_ONLY( void PushToRenderThread (ParallelOp &&op) );

		static Ptr< AndroidPlatform >	GetInstance ()		{ return _instance; }

		static void CreateInstance ();
		static void DestroyInstance ();


		void Init (int supportedSensors);
		void EnterForeground (bool completed);
		void EnterBackground (bool completed);
		void SurfaceChanged (const AndroidWindowSurface &surface, uint width, uint height);
		void SurfaceDestroyed ();
		void Resize (uint width, uint height);
		void VisibilityChanged (bool isVisible);
		void LowMemory ();
		void SetDirectory (uni_c_string dir, EDirectory::type type);
		void SetDevice (Device *dev);
		
		void OnKey (int keyCode, bool down);
		void OnTouch (bool down, int id, float x, float y, float pressure);
		void OnTouchMotion (int id, float x, float y, float pressure);

		void OnAcceleration (float x, float y, float z);
		void OnMagneticField (float x, float y, float z);
		void OnGyroscope (float x, float y, float z);
		void OnOrientation (float x, float y, float z);
		void OnRotation (float x, float y, float z);
		void OnLightSensor (float level);
		void OnProximitySensor (float distance);
		void OnSendEngineEvent (uint eventId, uint param0, uint param1, uint param2);

		void OnLocation (uint64 time, double lon, double lat, float alt, float accur, float speed);
		void SetLocationStatus (SysEvent::LocationStatus::EType state);

		static void OnLicenseChecked (const OnLicenseChecked_t &callback, ELicenseCheckResponse::type code, const string &error);

		void MessageResult (int messageID, int result);
		void AudioFocusChanged (SysEvent::Audio::EType state);

		void UpdateDisplayOrientation (Display::EOrientation orientation);

		void Iterate ();
		void IterateInRenderThread ();

		Ptr< AndroidJavaInterface >	GetJavaInterface ()		{ return _javaInterface.ptr(); }
		Ptr< IApplication >			GetApplication ()		{ return _application; }

		EDevice::type				GetDeviceType () const	{ return _requiredDeviceType; }


	private:
		SysEvent::time_t	_GetTimestamp ()	{ return _timer.GetTimeDelta(); }

		void _RenderLoop ();
		void _OnSurfaceChanged (const AndroidWindowSurface &surface);
		void _OnSurfaceDestroyed ();

		static void _RenderLoopProc (void *param);

		void _ExitRenderLoop ();
		void _OnExitRenderLoop ();

		void _SetDirectory (const string &dir, EDirectory::type dirType);
		void _SetDevice (Device *dev);
		void _SetDeviceResolution (const ivec2 &dim);

		void _MessageResult (int messageID, int result);
		
		void _OnEngineOutEvent (const SysEvent &ev);
		
		void _UpdateDisplayOrientation (Display::EOrientation orientation);

		static bool _ContextEGL_InitWindow (void *window, int format, void *parameter);
		static bool _ContextEGL_ReleaseWindow (void *window, void *parameter);
		static void _ContextEGL_OnContextLost (ContextEGL *context, void *parameter);


	// interface
	public:
		// Sensor //
		virtual bool EnableSensor (ESensor::type sensor) override						{ return _sensorManager.EnableSensor( sensor ); }
		virtual bool DisableSensor (ESensor::type sensor) override						{ return _sensorManager.DisableSensor( sensor ); }
		virtual bool IsSensorEnabled (ESensor::type sensor) override					{ return _sensorManager.IsSensorEnabled( sensor ); }
		virtual bool SetSensorUpdateRate (ESensor::type sensor, uint rate) override		{ return _sensorManager.SetSensorUpdateRate( sensor, rate ); }
		virtual bool HasSensor (ESensor::type sensor) override							{ return _sensorManager.HasSensor( sensor ); }

		// Render //
		virtual bool InitRender (INOUT VideoSettings &vs) override;
		virtual void SwapBuffers () override;
		virtual bool IsRenderingAvailable () override;
		
		// Audio //
		virtual void EnableAudio (bool enabled) override;
		
		// Extensions //
		virtual Ptr< HardwareCamera > GetCamera () override								{ return &_camera; }
		
		// License //
		virtual void CheckLicense (const OnLicenseChecked_t &callback, uni_c_string publicKey) override;

		// Other //
		virtual void Terminate () override;
		
		virtual void Vibrate (const uni_array<const int64> &pattern) override;

		virtual void SendEmail (uni_c_string address, uni_c_string subject,
								uni_c_string text, uni_c_string attachment) override;
		
		//virtual void BackgroundMode (bool enabled) override;

		virtual void InitDevice (EDevice::type deviceType) override;
		virtual void SetDisplayOrientation (EDisplayOrientation::type orientation) override;
		
		virtual void OpenURL (uni_c_string url) override;

		virtual void ShowNotification (uni_c_string text, bool longTime) override;

		virtual void ShowMessage (const OnMessageResult_t &callback,
								  uni_c_string caption, uni_c_string text,
								  uni_c_string positive, uni_c_string negative) override;

		virtual EventSystemPtr		GetEventSystem () override							{ return &_events; }
		virtual IParallelThreadPtr	GetThread ()	  override							{ return this; }
		
		virtual uni_c_string 		GetDirectoryPath (EDirectory::type type) override	{ return _directories[ type ]; }

		
	// IParallelThread //
	public:
		virtual bool IsCurrent () const override
		{
			return _thread.IsCurrent();
		}


		virtual void Flush () override
		{
			_messageQueue.Flush();
		}
		

		void Push (const ParallelOp &op) override
		{
			_messageQueue.Push( op );
		}


		RVALUE_REFERENCE_ONLY(
			void Push (ParallelOp &&op) override
			{
				_messageQueue.Push( ToRValueRef( op ) );
			}
		)


		virtual void Quit () override
		{
			WARNING( "not supported" );
			//Terminate();
		}


		virtual uint ProcessMessages () override
		{
			uint processed = _messageQueue.ProcessAll();
			return processed > 1 ? 16 : 66;
		}
	};
	
	
/*
=================================================
	OnKey
=================================================
*/
	inline void AndroidPlatform::OnKey (int keyCode, bool down)
	{
		TSysEvent< ESystemEvent::RAW_KEY >	se( _GetTimestamp() );

		se.rawKey.code		= keyCode;
		se.rawKey.pressed	= down;

		SendEvent( se );
	}
	
/*
=================================================
	OnTouch
=================================================
*/
	inline void AndroidPlatform::OnTouch (bool down, int id, float x, float y, float pressure)
	{
		TSysEvent< ESystemEvent::RAW_TOUCH >	se( _GetTimestamp() );

		se.rawTouch.id		 = id;
		se.rawTouch.x		 = x;
		se.rawTouch.y		 = y;
		se.rawTouch.pressure = pressure;
		se.rawTouch.motion	 = false;
		se.rawTouch.pressed	 = down;
		
		SendEvent( se );
	}
	
/*
=================================================
	OnTouchMotion
=================================================
*/
	inline void AndroidPlatform::OnTouchMotion (int id, float x, float y, float pressure)
	{
		TSysEvent< ESystemEvent::RAW_TOUCH >	se( _GetTimestamp() );

		se.rawTouch.id		 = id;
		se.rawTouch.x		 = x;
		se.rawTouch.y		 = y;
		se.rawTouch.pressure = pressure;
		se.rawTouch.motion	 = true;
		se.rawTouch.pressed	 = true;
		
		SendEvent( se );
	}
	
/*
=================================================
	OnAcceleration
=================================================
*/
	inline void AndroidPlatform::OnAcceleration (float x, float y, float z)
	{
		TSysEvent< ESystemEvent::ACCEL >	se( _GetTimestamp() );

		se.accel.x = x;
		se.accel.y = y;
		se.accel.z = z;
		
		SendEvent( se );
	}
	
/*
=================================================
	OnMagneticField
=================================================
*/
	inline void AndroidPlatform::OnMagneticField (float x, float y, float z)
	{
		TSysEvent< ESystemEvent::MAGNETIC >	se( _GetTimestamp() );

		se.magnetic.x = x;
		se.magnetic.y = y;
		se.magnetic.z = z;
		
		SendEvent( se );
	}
	
/*
=================================================
	OnGyroscope
=================================================
*/
	inline void AndroidPlatform::OnGyroscope (float x, float y, float z)
	{
		TSysEvent< ESystemEvent::GYROSCOPE >	se( _GetTimestamp() );

		se.gyro.x = x;
		se.gyro.y = y;
		se.gyro.z = z;
		
		SendEvent( se );
	}
	
/*
=================================================
	OnOrientation
=================================================
*/
	inline void AndroidPlatform::OnOrientation (float x, float y, float z)
	{
		TSysEvent< ESystemEvent::ORIENTATION >	se( _GetTimestamp() );

		se.orient.x = x;
		se.orient.y = y;
		se.orient.z = z;
		
		SendEvent( se );
	}

/*
=================================================
	OnRotation
=================================================
*/
	inline void AndroidPlatform::OnRotation (float x, float y, float z)
	{
		TSysEvent< ESystemEvent::ROTATION >	se( _GetTimestamp() );

		se.rotation.x = x;
		se.rotation.y = y;
		se.rotation.z = z;
		
		SendEvent( se );
	}
	
/*
=================================================
	OnLightSensor
=================================================
*/
	inline void AndroidPlatform::OnLightSensor (float level)
	{
		TSysEvent< ESystemEvent::LIGHT_SENSOR >	se( _GetTimestamp() );

		se.light.value = level;
		
		SendEvent( se );
	}
	
/*
=================================================
	OnProximitySensor
=================================================
*/
	inline void AndroidPlatform::OnProximitySensor (float distance)
	{
		TSysEvent< ESystemEvent::PROXIMITY_SENSOR >	se( _GetTimestamp() );

		se.prox.distance = distance;
		
		SendEvent( se );
	}
	
/*
=================================================
	OnSendEngineEvent
=================================================
*/
	inline void AndroidPlatform::OnSendEngineEvent (uint eventId, uint param0, uint param1, uint param2)
	{
		TSysEvent< ESystemEvent::ENGINE_IN >	se( _GetTimestamp() );

		se.engine.type		= eventId;
		se.engine.data[0]	= param0;
		se.engine.data[1]	= param1;
		se.engine.data[2]	= param2;
		STATIC_ASSERTE( COUNT_OF(se.engine.data) == 3 );

		SendEvent( se );
	}
	
/*
=================================================
	OnLocation
=================================================
*/
	inline void AndroidPlatform::OnLocation (uint64 time, double lon, double lat, float alt, float accur, float speed)
	{
		TSysEvent< ESystemEvent::LOCATION >	se( _GetTimestamp() );

		se.location.utcTime		= time;
		se.location.lat			= lat;
		se.location.lon			= lon;
		se.location.accuracy	= accur;
		se.location.altitude	= alt;
		se.location.speed		= speed;
		
		SendEvent( se );
	}
	
/*
=================================================
	SetLocationStatus
=================================================
*/
	inline void AndroidPlatform::SetLocationStatus (SysEvent::LocationStatus::EType state)
	{
		TSysEvent< ESystemEvent::LOCATION_STATUS >	se;

		se.locStatus.status = state;
		
		SendEvent( se );
	}


}	// Platforms
}	// Engine

#endif	// PLATFORM_ANDROID or ANDROID_EMULATOR